/* Copyright 2020,2022,2023 NXP
 *
 * NXP Confidential. This software is owned or controlled by NXP and may only
 * be used strictly in accordance with the applicable license terms.  By
 * expressly accepting such terms or by downloading, installing, activating
 * and/or otherwise using the software, you are agreeing that you have read,
 * and that you agree to comply with and are bound by, such license terms.  If
 * you do not agree to be bound by the applicable license terms, then you may
 * not retain, install, activate or otherwise use the software.
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "SwupApi.h"
#include "phUwb_BuildConfig.h"
#include "phNxpLogApis_Swup.h"
#include "phTmlUwb_transport.h"
#include "phOsalUwb.h"
#include "phUwbErrorCodes.h"

#define MAX_RETRY_COUNT 0x09
/** Local functions prototypes */
static int read_swup_response(uint8_t *rsp, uint16_t *rsp_len);

static int read_swup_response(uint8_t *rsp, uint16_t *rsp_len)
{
    int retry_count  = 0;
    uint16_t readLen = 0;

retry:
    if (retry_count > MAX_RETRY_COUNT) {
        return RETURNED_FAILURE;
    }
    readLen = phTmlUwb_rci_read(rsp, 0);
    if (readLen < 4) {
        phOsalUwb_Delay(100);
        retry_count++;
        goto retry;
    }
    else {
        *rsp_len = readLen;
        LOG_MAU8_I("SwupRx< ", rsp, *rsp_len);
        return RETURNED_SUCCESS;
    }
}

SwupResponseStatus_t Swup_TransferComponent(
    SwupStatus_t *swupStatus, const uint8_t componentIndex, const uint16_t segmentNumber, const uint8_t cmdData[])
{
    SwupResponseStatus_t error                                               = STATUS_GENERIC_ERROR;
    uint16_t resp_len                                                        = 0;
    uint8_t resp[256]                                                        = {0};
    uint8_t App_RciTxData[SWUP_RCI_HEADER_LEN + SWUP_LEN_TRANSFER_COMPONENT] = {
        SWUP_COMMAND, SWUP_SUB_CMD_TRANSFER_COMPONENT, SWUP_FRAME_TYPE, SWUP_LEN_TRANSFER_COMPONENT};
    uint8_t *ptr;

    LOG_I("%s #%d", __FUNCTION__ + 5, segmentNumber);
    /*send SWUP_SUB_CMD_TRANSFER_COMPONENT Command*/
    if (cmdData == NULL)
        return error;
    App_RciTxData[SWUP_RCI_DATA_OFFSET]     = componentIndex;
    App_RciTxData[SWUP_RCI_DATA_OFFSET + 1] = (uint8_t)(segmentNumber >> 8 * 0);
    App_RciTxData[SWUP_RCI_DATA_OFFSET + 2] = (uint8_t)(segmentNumber >> 8 * 1);
    phOsalUwb_MemCopy(&App_RciTxData[SWUP_RCI_DATA_OFFSET + 3], &cmdData[0], TRANSFER_COMPONENT_SEGMENT_SIZE);

    LOG_MAU8_D("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1])) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    {
        uint32_t u32SwupStatus;
        STREAM_TO_uint32_t(u32SwupStatus, ptr);
        *swupStatus = (SwupStatus_t)u32SwupStatus;
    }
    return error;
}

SwupResponseStatus_t Swup_TransferManifest(
    SwupStatus_t *swupStatus, const uint8_t manifestIndex, const uint8_t cmdData[])
{
    SwupResponseStatus_t error                                              = STATUS_GENERIC_ERROR;
    uint16_t resp_len                                                       = 0;
    uint8_t resp[256]                                                       = {0};
    uint8_t App_RciTxData[SWUP_RCI_HEADER_LEN + SWUP_LEN_TRANSFER_MANIFEST] = {
        SWUP_COMMAND, SWUP_SUB_CMD_TRANSFER_MANIFEST, SWUP_FRAME_TYPE, SWUP_LEN_TRANSFER_MANIFEST};
    uint8_t *ptr;
    LOG_I("%s #%d", __FUNCTION__ + 5, manifestIndex);

    if (cmdData == NULL)
        return error;
    /*send SWUP_SUB_CMD_TRANSFER_MANIFEST Command*/
    App_RciTxData[SWUP_RCI_DATA_OFFSET] = manifestIndex;
    phOsalUwb_MemCopy(&App_RciTxData[SWUP_RCI_DATA_OFFSET + 1], &cmdData[0], TRANSFER_MANIFEST_SEGMENT_SIZE);

    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1])) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    {
        uint32_t u32SwupStatus;
        STREAM_TO_uint32_t(u32SwupStatus, ptr);
        *swupStatus = (SwupStatus_t)u32SwupStatus;
    }
    return error;
}

SwupResponseStatus_t Swup_VerifyComponent(SwupStatus_t *swupStatus, const uint8_t componentIndex)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;
    uint16_t resp_len          = 0;
    uint8_t resp[256]          = {0};
    uint8_t *ptr;

    /*send SWUP_SUB_CMD_VERIFY_COMPONENT Command*/
    uint8_t App_RciTxData[SWUP_RCI_HEADER_LEN + 1] = {
        SWUP_COMMAND, SWUP_SUB_CMD_VERIFY_COMPONENT, SWUP_FRAME_TYPE, SWUP_LEN_VERIFY_COMPONENT};
    App_RciTxData[SWUP_RCI_HEADER_LEN] = componentIndex;

    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1])) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    {
        uint32_t u32SwupStatus;
        STREAM_TO_uint32_t(u32SwupStatus, ptr);
        *swupStatus = (SwupStatus_t)u32SwupStatus;
    }
    return error;
}

SwupResponseStatus_t Swup_VerifyAll(SwupStatus_t *swupStatus)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;
    uint16_t resp_len          = 0;
    uint8_t resp[256]          = {0};

    /*send SWUP_SUB_CMD_VERIFY_ALL Command*/
    uint8_t App_RciTxData[] = {
        SWUP_COMMAND,
        SWUP_SUB_CMD_VERIFY_ALL,
        SWUP_FRAME_TYPE,
        SWUP_LEN_VERIFY_ALL,
    };
    uint8_t *ptr;

    LOG_I("%s", __FUNCTION__ + 5);
    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1])) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    {
        uint32_t u32SwupStatus;
        STREAM_TO_uint32_t(u32SwupStatus, ptr);
        *swupStatus = (SwupStatus_t)u32SwupStatus;
    }
    return error;
}

SwupResponseStatus_t Swup_FinishUpdate(SwupStatus_t *swupStatus)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;
    uint16_t resp_len          = 0;
    uint8_t resp[256]          = {0};

    /*send SWUP_SUB_CMD_FINISH_UPDATE Command*/
    uint8_t App_RciTxData[] = {
        SWUP_COMMAND,
        SWUP_SUB_CMD_FINISH_UPDATE,
        SWUP_FRAME_TYPE,
        SWUP_LEN_FINISH_UPDATE,
    };
    uint8_t *ptr;

    LOG_I("%s", __FUNCTION__ + 5);
    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1])) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    {
        uint32_t u32SwupStatus;
        STREAM_TO_uint32_t(u32SwupStatus, ptr);
        *swupStatus = (SwupStatus_t)u32SwupStatus;
    }
    return error;
}

SwupResponseStatus_t Swup_StartUpdate(SwupStatus_t *swupStatus)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;
    uint16_t resp_len          = 0;
    uint8_t resp[256]          = {0};
    uint8_t *ptr;

    /*send SWUP_SUB_CMD_START_UPDATE Command*/
    uint8_t App_RciTxData[] = {
        SWUP_COMMAND,
        SWUP_SUB_CMD_START_UPDATE,
        SWUP_FRAME_TYPE,
        SWUP_LEN_START_UPDATE,
    };

    LOG_I("%s", __FUNCTION__ + 5);
    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1])) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    {
        uint32_t u32SwupStatus;
        STREAM_TO_uint32_t(u32SwupStatus, ptr);
        *swupStatus = (SwupStatus_t)u32SwupStatus;
    }
    return error;
}

SwupResponseStatus_t Swup_ClearRamManifest(SwupStatus_t *swupStatus)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;
    uint16_t resp_len          = 0;
    uint8_t resp[256]          = {0};

    /*send SWUP_SUB_CMD_CLEAR_RAM_MANIFEST Command*/
    uint8_t App_RciTxData[] = {
        SWUP_COMMAND,
        SWUP_SUB_CMD_CLEAR_RAM_MANIFEST,
        SWUP_FRAME_TYPE,
        SWUP_LEN_CLEAR_RAM_MANIFEST,
    };
    uint8_t *ptr;

    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1])) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    {
        uint32_t u32SwupStatus;
        STREAM_TO_uint32_t(u32SwupStatus, ptr);
        *swupStatus = (SwupStatus_t)u32SwupStatus;
    }
    return error;
}

tUWBAPI_STATUS Swup_Deactivate(SwupStatus_t swupStatus)
{
    tUWBAPI_STATUS retStatus = UWBAPI_STATUS_FAILED;
    uint16_t ntf_len         = 0;
    uint8_t ntf_buffer[5]    = {0};

    /**
     * This api send the DEACTIVATE command and we expect Device status notification not swup response.
     * Hence after sending phTmlUwb_rci_write we are using phTmlUwb_uci_read to read the Device status notification.
     *
     */

    uint8_t App_RciTxData[] = {
        SWUP_COMMAND,
        SWUP_SUB_CMD_DEACTIVATE,
        SWUP_FRAME_TYPE,
        SWUP_LEN_DEACTIVATE_ID,
    };

    LOG_I("%s", __FUNCTION__ + 5);

    if (swupStatus != SWUP_STATUS_ACTIVE) {
        LOG_E("Device Not in SWUP_STATUS_ACTIVE state");
        LOG_E("This API will support if the Device in Active state");
        goto cleanup;
    }

    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));

    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        LOG_E("phTmlUwb_rci_write failed");
        return retStatus;
    }

    ntf_len = phTmlUwb_uci_read(ntf_buffer, sizeof(ntf_buffer));

    if (ntf_len != 5) {
        LOG_E("Not enough length received");
        goto cleanup;
    }
    else if ((*ntf_buffer == 0x60) && (*(ntf_buffer + 1) == 0x01) && (*(ntf_buffer + 4) == 0xFC)) {
        /* Device is in HPD */
        LOG_E("Device went to HPD state");
        goto cleanup;
    }
    else if ((*ntf_buffer == 0x60) && (*(ntf_buffer + 1) == 0x01) && (*(ntf_buffer + 4) == 0x01)) {
        LOG_MAU8_I("UCI-RX>>", ntf_buffer, ntf_len);
        retStatus = UWBAPI_STATUS_OK;
        goto cleanup;
    }
    else {
        LOG_E("unexpected Device status notification received");
        LOG_MAU8_E("UCI-RX>>", ntf_buffer, ntf_len);
    }
cleanup:
    return retStatus;
}

SwupResponseStatus_t Swup_GetDeviceInfo(SwupDeviceInfo_t *swupDeviceInfo)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;
    uint16_t resp_len          = 0;
    uint8_t resp[256]          = {0};
    uint8_t *ptr;
    /*send SWUP_SUB_CMD_GET_DEVICE_INFO Command*/
    uint8_t App_RciTxData[] = {
        SWUP_COMMAND,
        SWUP_SUB_CMD_GET_DEVICE_INFO,
        SWUP_FRAME_TYPE,
        SWUP_LEN_GET_DEVICE_INFO,
    };

    if (swupDeviceInfo == NULL) {
        return error;
    }

    LOG_I("%s", __FUNCTION__ + 5);

    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1]) || (*(resp + 3) != 0x2C)) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    STREAM_TO_uint32_t(swupDeviceInfo->swupStatus, ptr);
    if (error == STATUS_CMD_SUCCESS) {
        /* Ptr will be automatically incremented based on the size */
        STREAM_TO_ARRAY(&swupDeviceInfo->productId[0], ptr, sizeof(swupDeviceInfo->productId));
        STREAM_TO_uint32_t(swupDeviceInfo->hardwareId, ptr);
        STREAM_TO_ARRAY(&swupDeviceInfo->typeCheckId[0], ptr, sizeof(swupDeviceInfo->typeCheckId));
        STREAM_TO_ARRAY(&swupDeviceInfo->romId[0], ptr, sizeof(swupDeviceInfo->romId));
        STREAM_TO_ARRAY(&swupDeviceInfo->swupVersion[0], ptr, sizeof(swupDeviceInfo->swupVersion));
    }
    return error;
}

void Swup_FlushResponse(void)
{
#if !defined(_MSC_VER)
    /* Flush out last response */
    uint16_t resp_len = 0;
    uint8_t resp[256] = {0};
    resp_len          = sizeof(resp);
    (void)read_swup_response(resp, &resp_len);
#endif
}

SwupResponseStatus_t Swup_ReadDeviceId(SwupDeviceId_t *swupDeviceId)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;
    uint16_t resp_len          = 0;
    uint8_t resp[256]          = {0};
    uint8_t *ptr;

    /*send SWUP_SUB_CMD_READ_DEVICE_ID Command*/
    uint8_t App_RciTxData[] = {
        SWUP_COMMAND,
        SWUP_SUB_CMD_READ_DEVICE_ID,
        SWUP_FRAME_TYPE,
        SWUP_LEN_READ_DEVICE_ID,
    };
    if (swupDeviceId == NULL) {
        return error;
    }

    LOG_I("%s", __FUNCTION__ + 5);
    Swup_FlushResponse();

    LOG_MAU8_I("SwupTx> ", App_RciTxData, sizeof(App_RciTxData));
    if (phTmlUwb_rci_write(App_RciTxData, sizeof(App_RciTxData)) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != App_RciTxData[0]) || (*(resp + 1) != App_RciTxData[1]) || (*(resp + 3) != 0x1C)) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }
    STREAM_TO_uint32_t(swupDeviceId->swupStatus, ptr);
    if (error == STATUS_CMD_SUCCESS) {
        /* Ptr will be automatically incremented based on the size */
        STREAM_TO_ARRAY(&swupDeviceId->waferId[0], ptr, sizeof(swupDeviceId->waferId));
        STREAM_TO_ARRAY(&swupDeviceId->waferNumber, ptr, sizeof(swupDeviceId->waferNumber));
        STREAM_TO_uint16_t(swupDeviceId->waferCoordinateX, ptr);
        STREAM_TO_uint16_t(swupDeviceId->waferCoordinateY, ptr);
        STREAM_TO_uint32_t(swupDeviceId->serialNumber, ptr);
    }
    return error;
}

/**
 * Run generic command
 */
SwupResponseStatus_t Swup_GeneralCmd(uint8_t *pCmd, uint8_t cmdLen, uint8_t *pRsp, uint16_t *pInOutLen)
{
    SwupResponseStatus_t error = STATUS_GENERIC_ERROR;

    uint16_t resp_len = 0;
    uint8_t resp[256] = {0};
    uint8_t *ptr;

    if (NULL == pInOutLen) {
        return error;
    }
    if (NULL == pCmd) {
        return error;
    }

    if (pRsp == NULL) {
        return error;
    }

    LOG_I("%s", __FUNCTION__ + 5);

    LOG_MAU8_I("SwupTx> ", pCmd, cmdLen);
    if (phTmlUwb_rci_write(pCmd, cmdLen) != 0) {
        return error;
    }

    if (read_swup_response(resp, &resp_len) != RETURNED_SUCCESS) {
        return error;
    }
    if ((*resp != pCmd[0]) || (*(resp + 1) != pCmd[1])) {
        return error;
    }
    if (*(resp + 3) != (*pInOutLen)) {
        return error;
    }

    ptr = &resp[SWUP_RCI_DATA_OFFSET];
    /* Ptr will be automatically incremented based on the size */
    {
        uint32_t u32error;
        STREAM_TO_uint32_t(u32error, ptr);
        error = (SwupResponseStatus_t)u32error;
    }

    if ((SWUP_RCI_DATA_OFFSET + *pInOutLen) >= resp_len) {
        /* Ptr will be automatically incremented based on the size */
        phOsalUwb_MemCopy(pRsp, resp + SWUP_RCI_DATA_OFFSET, resp_len - SWUP_RCI_DATA_OFFSET);
        *pInOutLen = resp_len - SWUP_RCI_DATA_OFFSET;
    }
    else {
        error = STATUS_COMM_INCOMPLETE_TRANSFER;
    }

    return error;
}
